using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Linq;
using System.Xml.Linq;
using System.Windows.Forms;
using System.Drawing;
using System.Diagnostics;

namespace GuavaData
{
    [DebuggerVisualizer(typeof(GTreeVisualizer))]
    [Serializable]
    public class GTree : Object, Guava
    {
        List<GTree> gt;
        public GTree parent;
        public Type ctl;
        private LaunchType ltype;
        private ControlType ctype;
        private string gtext;
        private string gname;
        private System.Drawing.Point glocation;
        private System.Drawing.Size gsize;
        private string gdefault;
        private string gtooltip;
        private GuavaData.Domain.Domain gdomain;

        public GTree(Guava g)
        {
            this.gt = new List<GTree>();
            this.parent = null;
            this.ctl = g.GetType();
            this.ltype = g.LType;
            this.ctype = g.CType;
            this.gtext = g.GText;
            this.gname = g.GName;
            this.glocation = g.GLocation;
            this.gsize = g.GSize;
            this.gdefault = g.GDefault;
            this.gtooltip = g.GToolTip;
            this.gdomain = g.GDomain;
        }

        public GQueryNode ToBuildTree()
        {
            // Create the first TreeNode-Root
            GQueryNode rootnode = new GQueryNode(this);
            this.AddNodes(rootnode);
            return rootnode;
        }
        
        private void AddNodes(GQueryNode node)
        {
            if ((node.g.CType == ControlType.Entity) || (node.g.CType == ControlType.Extern))
                AttachSemanticSpike(node);
            foreach (GTree temp in this.gt)
            {
                GQueryNode newnode = new GQueryNode(temp);
                newnode.g.parent = node.g;
                temp.AddNodes(newnode);
                //attach child node to its parent
                node.Nodes.Add(newnode);
            }
        }

        private void AttachSemanticSpike(GQueryNode node)
        {
            List<SemanticUnit> ls = AppData.c.SpikeForSemanticChanges(node.g.GName);

            foreach (SemanticUnit s in ls)
            {
                if (s is AddColumnUnit)
                {
                    AddColumnUnit acu = s as AddColumnUnit;
                    GQueryNode gqn = new GQueryNode(new GTree(acu.Column));
                    gqn.g.parent = node.g;
                    node.Nodes.Add(gqn);
                    node.semanticAdditions.Add(gqn);
                }
            }
        }

        public static GTree Generate(Guava gin)
        {
            GTree root = new GTree(gin);

            foreach (Guava g in gin.GComponents)
            {
                GTree temp = GTree.Generate(g);
                temp.parent = root;
                root.gt.Add(temp);
            }

            return root;
        }

        public System.Data.DataSet ToDataSet()
        {
            if (this.CType != ControlType.Entity) throw new Exception("Method ToDataSet can only be called on Entity nodes, and node " + this.GName + " is of category " + this.CType.ToString());
            System.Data.DataSet ds = new System.Data.DataSet("Naive");

            // New table name comes directly from the name of the GTree node
            //System.Data.DataTable dt = new System.Data.DataTable(this.Name);
            //ds.Tables.Add(dt);

            // Add new artificial primary key column
            // Since this is the root table, there are no fk's
            //dt.Columns.Add("id", Guid.NewGuid().GetType());

            ParseDataSet(ds, null, LaunchType.Root);

            return ds;
        }

        private void ParseDataSet(System.Data.DataSet ds, System.Data.DataTable dt, LaunchType rel)
        {
            System.Data.DataColumn dc;
            switch (this.CType)
            {
                case ControlType.None:
                    foreach (GTree t in this.gt)
                        t.ParseDataSet(ds, dt, this.LType);
                    break;

                case ControlType.Attribute:
                    if (!(dt.Columns.Contains(this.GName)))
                    {
                        dc = new System.Data.DataColumn(this.GName, this.GDomain.NaiveType);
                        dc.ExtendedProperties[Operator.DomainFlag] = this.GDomain;
                        dc.ExtendedProperties[Operator.GuavaFlag] = this;
                        dt.Columns.Add(dc);
                    }
                    foreach (GTree t in this.gt)
                        t.ParseDataSet(ds, dt, this.LType);
                    break;

                case ControlType.Container:
                    foreach (GTree t in this.gt)
                        t.ParseDataSet(ds, dt, rel);
                    break;

                case ControlType.Entity:
                    if (!(ds.Tables.Contains(this.GName)))
                    {
                        System.Data.DataTable newTable = new System.Data.DataTable(this.GName);
                        ds.Tables.Add(newTable);

                        if (rel == LaunchType.Multi)
                        {
                            dc = new System.Data.DataColumn("id", typeof(Guid));
                            dc.ExtendedProperties[Operator.DomainFlag] = new Domain.UniqueIdentifier();
                            dc.ExtendedProperties[Operator.GuavaFlag] = this;
                            newTable.Columns.Add(dc);

                            dc = new System.Data.DataColumn("fk", typeof(Guid));
                            dc.ExtendedProperties[Operator.DomainFlag] = new Domain.UniqueIdentifier();
                            newTable.Columns.Add(dc);
                        }
                        else if (rel == LaunchType.Single)
                        {
                            dc = new System.Data.DataColumn("id", typeof(Guid));
                            dc.ExtendedProperties[Operator.DomainFlag] = new Domain.UniqueIdentifier();
                            dc.ExtendedProperties[Operator.GuavaFlag] = this;
                            newTable.Columns.Add(dc);
                        }
                        else if (rel == LaunchType.NA)
                        {
                            dc = new System.Data.DataColumn("id", typeof(Guid));
                            dc.ExtendedProperties[Operator.DomainFlag] = new Domain.UniqueIdentifier();
                            dc.ExtendedProperties[Operator.GuavaFlag] = this;
                            newTable.Columns.Add(dc);

                            dc = new System.Data.DataColumn("fk", typeof(Guid));
                            dc.ExtendedProperties[Operator.DomainFlag] = new Domain.UniqueIdentifier();
                            newTable.Columns.Add(dc);
                        }
                        else
                        {
                            dc = new System.Data.DataColumn("id", typeof(Guid));
                            dc.ExtendedProperties[Operator.DomainFlag] = new Domain.UniqueIdentifier();
                            dc.ExtendedProperties[Operator.GuavaFlag] = this;
                            newTable.Columns.Add(dc);
                        }

                        foreach (GTree t in this.gt)
                            t.ParseDataSet(ds, newTable, this.LType);
                    }
                    break;
                case ControlType.Extern:
                    if (!(ds.Tables.Contains(this.GName)))
                    {
                        System.Data.DataTable newTable = new System.Data.DataTable(this.GName);
                        ds.Tables.Add(newTable);
                        dc = new System.Data.DataColumn("id", typeof(Guid));
                        dc.ExtendedProperties[Operator.DomainFlag] = new Domain.UniqueIdentifier();
                        dc.ExtendedProperties[Operator.GuavaFlag] = this;
                        newTable.Columns.Add(dc);
                        foreach (GTree t in this.gt)
                            t.ParseDataSet(ds, newTable, LaunchType.NA);
                    }
                    break;
            }

            return;
        }

        public XmlDocument ToXML()
        {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml("<GTree></GTree>");

            // Start recursive process of building XML tree
            this.ParseXML(doc, doc.DocumentElement);

            return doc;
        }

        private void ParseXML(XmlDocument doc, XmlElement e)
        {
           
            XmlElement ee = doc.CreateElement(this.GName);
            ee.SetAttribute("Control", this.ctl.ToString());
            ee.SetAttribute("ControlType", this.CType.ToString());
            ee.SetAttribute("Width", this.GSize.Width.ToString());
            ee.SetAttribute("Height", this.GSize.Height.ToString());
            ee.SetAttribute("X", this.GLocation.X.ToString());
            ee.SetAttribute("Y", this.GLocation.Y.ToString());
            ee.SetAttribute("Text", this.GText);
            ee.SetAttribute("launchType", this.LType.ToString());
            ee.SetAttribute("print", "");
            ee.SetAttribute("condition", "");
            if (this.GDomain != null)
            {

                ee.SetAttribute("Type", this.GDomain.DBType);

                if (this.GDomain is GuavaData.Domain.Enumerated)
                {
                    
                   XmlElement el =  this.GDomain.ToXml(doc);
                   ee.AppendChild(el);
                }
            }
            else
            {
                ee.SetAttribute("Type", null);
            }
            e.AppendChild(ee);

            foreach (GTree g in this.gt)
            {
                g.ParseXML(doc, ee);
            }
        }

        public Schema ToSchema()
        {
            if (this.CType != ControlType.Entity) throw new Exception("Method ToSchema can only be called on Entity nodes, and node " + this.GName + " is of category " + this.CType.ToString());
            Schema curr = new Schema();

            // New table name comes directly from the name of the GTree node
            Scheme temp = curr.newScheme(this.GName, null);

            // Add new artificial primary key column
            // Since this is the root table, there are no fk's
            temp.newColumn("id", new GuavaData.Domain.UniqueIdentifier (), true, false);

            foreach (GTree t in this.gt)
            {
                t.ParseSchema(curr, temp, LaunchType.NA);
            }

            return curr;
        }

        private void ParseSchema(Schema curr, Scheme table, LaunchType rel)
        {
            switch (this.CType)
            {
                case ControlType.None:
                    foreach (GTree t in this.gt)
                        t.ParseSchema(curr, table, this.LType);
                    break;

                case ControlType.Attribute:
                    table.newColumn(this.GName, this.GDomain, false, false);
                    foreach (GTree t in this.gt)
                        t.ParseSchema(curr, table, this.LType);
                    break;

                case ControlType.Container:
                    foreach (GTree t in this.gt)
                        t.ParseSchema(curr, table, rel);
                    break;

                case ControlType.Entity:
                    Scheme newTable = curr.newScheme(this.GName, table);
                    if (rel == LaunchType.Multi)
                    {
                        newTable.newColumn("id", new GuavaData.Domain.UniqueIdentifier(), true, false);
                        newTable.newColumn("fk", new GuavaData.Domain.UniqueIdentifier(), false, true);
                    }
                    else if (rel == LaunchType.Single)
                    {
                        newTable.newColumn("id", new GuavaData.Domain.UniqueIdentifier(), true, true);
                    }
                    else if (rel == LaunchType.NA)
                    {
                        newTable.newColumn("id", new GuavaData.Domain.UniqueIdentifier(), true, false);
                        newTable.newColumn("fk", new GuavaData.Domain.UniqueIdentifier(), false, true);
                    }
                    foreach (GTree t in this.gt)
                        t.ParseSchema(curr, newTable, this.LType);
                    break;

                case ControlType.Extern:
                    newTable = curr.newScheme(this.GName, table);
                    newTable.newColumn("id", new GuavaData.Domain.UniqueIdentifier(), true, true);
                    foreach (GTree t in this.gt)
                        t.ParseSchema(curr, newTable, LaunchType.NA);
                    break;
            }

            return;
        }

        public string GetEntity()
        {
            GTree gtemp = this;

            while (gtemp.CType != ControlType.Entity) gtemp = gtemp.parent;

            return gtemp.GName;
        }
        
        public override string ToString()
        {
            string sTemp;
            GTree gTemp;

            sTemp = this.GName;
            gTemp = this.parent;

            while (gTemp != null)
            {
                sTemp = gTemp.GName + "/" + sTemp;
                gTemp = gTemp.parent;
            }

            return sTemp;
        }
        #region Guava Members

        public LaunchType LType
        {
            get { return this.ltype; }
        }

        public ControlType CType
        {
            get { return this.ctype; }
        }

        public string GText
        {
            get { return this.gtext; }
        }

        public string GName
        {
            get
            {
                return this.gname;
            }
            set
            {
                throw new Exception("Cannot set properties of a Guava component through a tree element.");
            }
        }

        public System.Drawing.Point GLocation
        {
            get { return this.glocation; }
        }

        public System.Drawing.Size GSize
        {
            get { return this.gsize; }
        }

        public List<Guava> GComponents
        {
            get 
            {
                List<Guava> lg = new List<Guava>();
                foreach (GTree g in this.gt) lg.Add(g);
                return lg;
            }
        }

        public void GBind(string table, int i)
        {
            return;
        }

        public string GDefault
        {
            get { return this.gdefault; }
        }

        public string GToolTip
        {
            get { return this.gtooltip; }
        }

        public GuavaData.Domain.Domain GDomain
        {
            get
            {
                return this.gdomain;
            }
        }

        public Guava this[string s]
        {
            get
            {
                foreach (Guava g in this.GComponents) if (g.GName == s) return g;
                return null;
            }
        }
        #endregion
    }

    public class GQueryNode : TreeNode
    {
        internal List<GQueryNode> semanticAdditions;
        public bool Print;
        bool isValue;
        public GTree g;
        string matchedProp;
        bool found;
        public Condition gqCondition;

        // Transient flags for query processing
        bool inQuery;

        public GQueryNode()
        {
            Print = false;
            found = false;
            inQuery = false;
            matchedProp = null;
            gqCondition = null;
            semanticAdditions = new List<GQueryNode>();
        }

        public GQueryNode(GTree gin)
            : this()
        {
            this.g = gin;
            if (g.CType == ControlType.Attribute) isValue = true;
            else isValue = false;
            this.Text = gin.GName;
        }

        public override string ToString()
        {
            string sTemp;
            GTree gTemp;

            sTemp = g.GName;
            gTemp = g.parent;

            while (gTemp != null)
            {
                sTemp = gTemp.GName + "/" + sTemp;
                gTemp = gTemp.parent;
            }

            return sTemp;
        }

        public bool IsValue
        {
            get
            {
                return this.isValue;
            }
        }

        public void TraverseNFindNodes(TreeView trv, string value, string condition)
        {
            foreach (GQueryNode temp in trv.Nodes)
                temp.setAttributesRec(value, condition);
        }

        private void setAttributesRec(string sparam, string scond)
        {
            this.setAttributes(sparam, scond);
            foreach (GQueryNode temp in this.Nodes)
                temp.setAttributesRec(sparam, scond);
        }

        private void setAttributes(string srchText, string srchCond)
        {
            this.matchedProp = null;
            this.found = false;

            if (srchCond.Equals("EQUAL TO"))
            {
                if (this.g.GName != null)
                {
                    if (srchText.ToLower().Equals(this.g.GName.ToString().ToLower()))
                    {
                        this.matchedProp += " : " + "Name";
                        this.found = true;
                    }

                }
                if (this.g.GText != null)
                {
                    if (srchText.ToLower().Equals(this.g.GText.ToString().ToLower()))
                    {
                        this.matchedProp += " : " + "Text";
                        this.found = true;
                    }
                }
                if ((this.g.GDomain != null) && (this.g.GDomain.DBType != null))
                {
                    if (srchText.ToLower().Equals(this.g.GDomain.DBType.ToString().ToLower()))
                    {
                        this.matchedProp += " : " + "Domain.DBType";
                        this.found = true;
                    }
                }
                if (this.g.GToolTip != null)
                {
                    if (srchText.ToLower().Equals(this.g.GToolTip.ToString().ToLower()))
                    {
                        this.matchedProp += " : " + "ToolTip";
                        this.found = true;
                    }
                }
                if (this.g.GDefault != null)
                {
                    if (srchText.ToLower().Equals(this.g.GDefault.ToString().ToLower()))
                    {
                        this.matchedProp += " : " + "Default";
                        this.found = true;
                    }
                }
            }
            else
            {
                if (srchCond.Equals("CONTAINS"))
                {
                    if (this.g.GName != null)
                    {
                        if (this.g.GName.ToString().ToLower().Contains(srchText.ToLower()))
                        {
                            this.matchedProp += " : " + "Name";
                            this.found = true;
                        }
                    }
                    if (this.g.GText != null)
                    {
                        if (this.g.GText.ToString().ToLower().Contains(srchText.ToLower()))
                        {
                            this.matchedProp += " : " + "Text";
                            this.found = true;
                        }
                    }
                    if ((this.g.GDomain != null) && (this.g.GDomain.DBType != null))
                    {
                        if (this.g.GDomain.DBType.ToString().ToLower().Contains(srchText.ToLower()))
                        {
                            this.matchedProp += " : " + "Domain.DBType";
                            this.found = true;
                        }
                    }
                    if (this.g.GToolTip != null)
                    {
                        if (this.g.GToolTip.ToString().ToLower().Contains(srchText.ToLower()))
                        {
                            this.matchedProp += " : " + "ToolTip";
                            this.found = true;
                        }
                    }
                    if (this.g.GDefault != null)
                    {
                        if (this.g.GDefault.ToString().ToLower().Contains(srchText.ToLower()))
                        {
                            this.matchedProp += " : " + "Default";
                            this.found = true;
                        }
                    }
                }
            }//else
        }

        public void TraverseNSetNodes(TreeView trv, Char mode)
        {
            foreach (GQueryNode temp in trv.Nodes)
                temp.setNodesAppearance(mode, trv);
        }

        private void setNodesAppearance(char mode, TreeView trv)
        {
            this.setNode(mode, trv);
            foreach (GQueryNode temp in this.Nodes)
                temp.setNodesAppearance(mode, trv);
        }
        
        private void setNode(char mode, TreeView trv)
        {
            List<string> nodesinpath = new List<string>();
            if (mode == 'C')
            {
                this.BackColor = Color.White;
                if (this.g.CType == ControlType.Container || this.g.CType == ControlType.None) this.Text = String.Concat("(", this.g.GName, ")");
                else this.Text = this.g.GName;
                this.Collapse();
            }
            else if ((mode == 'S') && (this.found))
            {
                nodesinpath.AddRange(this.FullPath.Split('\\'));
                if (nodesinpath != null)
                    TraverseNExpand(nodesinpath, trv);
            }
        }
        
        private void TraverseNExpand(List<string> nodes, TreeView trv)
        {
            foreach (GQueryNode temp in trv.Nodes)
                temp.ExpandChildNodes(nodes);
        }
        
        private void ExpandChildNodes(List<string> nodes)
        {
            this.ExpandNode(nodes);
            foreach (GQueryNode temp in this.Nodes)
                temp.ExpandChildNodes(nodes);
        }

        private void ExpandNode(List<string> fullpath)
        {
            foreach (string str in fullpath)
            {
                if (str.Equals(this.g.GName))
                    this.Expand();
                if ((this.matchedProp != null) && (this.Text.Equals(this.g.GName.ToString())))
                {
                    this.Text += this.matchedProp;
                    this.BackColor = Color.YellowGreen;
                }
            }
        }
        
        public void ResetGTreeView(TreeView trv)
        {
            foreach (GQueryNode temp in trv.Nodes)
                temp.ResetChildNodes();
        }

        private void ResetChildNodes()
        {
            this.Print = false;
            this.found = false;
            this.matchedProp = null;
            this.gqCondition = null;
            this.BackColor = Color.White;
            this.Text = this.g.GName;
            this.Collapse();
            this.Checked = false;
            foreach (GQueryNode temp in this.Nodes)
                temp.ResetChildNodes();
        }

        public Scheme ToQueryOutputScheme()
        {
            TreeView trv = this.TreeView;
            Scheme s = new Scheme("Query", null);

            foreach (GQueryNode temp in trv.Nodes)
                temp.visitToQueryOutputScheme(s);

            return s;
        }

        private void visitToQueryOutputScheme(Scheme s)
        {
            if (this.Checked)
            {
                // Add node to output schema
                GTree gt = this.g;
                if (gt.CType == ControlType.Entity)
                {
                    s.newColumn(String.Concat(gt.GName, ".id"), gt.GDomain, false, false);
                }
                else
                {
                    s.newColumn(String.Concat(gt.GetEntity(), ".", gt.GName), gt.GDomain, false, false);
                }
            }
            
            foreach (GQueryNode temp in this.Nodes)
                temp.visitToQueryOutputScheme(s);
        }

        public static Command toQuery(TreeView trv)
        {
            Command command = new Command();
            List<Operator> operators = new List<Operator>();

            // Loop through each of the root nodes
            foreach (GQueryNode temp in trv.Nodes)
            {
                // Recursively walk through the tree's nodes and process them
                temp.markQueryNodes();

                // Prune the top of the tree so that there are no unnecessary joins at the top
                GQueryNode queryRoot = temp.pruneQueryMarks();
                if (queryRoot == null) continue;

                // Make sure that the query root is a node of appropriate type
                while (queryRoot.g.CType != ControlType.Entity && queryRoot.g.CType != ControlType.Extern)
                {
                    queryRoot = queryRoot.Parent as GQueryNode;
                    if (queryRoot == null)
                        break;
                    queryRoot.inQuery = true;
                }
                if (queryRoot == null)
                    continue;

                // Create a new command to house the query
                List<string> projectList = new List<string>();
                string alias = null;
                Operator op = queryRoot.toOperator(command, null, projectList, ref alias);
                operators.Add(new Project(op, projectList));
            }

            // If there are no operators in the list, return null
            if (operators.Count == 0)
                return null;

            // Otherwise, create the cross-product of the operators
            command.root = operators[0];
            for (int i = 1; i < operators.Count; i++)
                command.root = new CrossJoin(command.root, operators[i]);
            return command;
        }

        private Operator toOperator(Command c, Operator o, List<string> projectList, ref string currentAlias)
        {
            // If the input parameter is null, then we're an entity-type node and we need to generate a table reference
            Operator working;
            if (o == null)
                working = this.createTableReference(c, ref currentAlias);
            else
                working = o;

            // If we need to be printed, add the appropriate print flag
            if (this.Print)
            {
                // If we have an print on a table node, add the id column
                if (this.g.CType == ControlType.Extern || this.g.CType == ControlType.Entity)
                    projectList.Add(currentAlias + ".id");
                else
                    projectList.Add(currentAlias + "." + this.g.GName);
            }

            // If we have a condition, add the condition as a filter node
            if (this.gqCondition != null)
            {
                working = new Filter(working);
                (working as Filter).AddCondition(this.g.GName, this.gqCondition);
            }

            List<GQueryNode> toJoin = new List<GQueryNode>();
            foreach (GQueryNode gqn in this.Nodes)
            {
                if (gqn.g.CType == ControlType.Extern || gqn.g.CType == ControlType.Entity)
                {
                    if (gqn.inQuery)
                    {
                        // Process the child
                        string newAlias = null, childKey, parentKey;
                        Operator childOp = gqn.toOperator(c, null, projectList, ref newAlias);

                        // Determine the join attributes
                        if (this.g.LType == LaunchType.Single)
                            childKey = newAlias + ".id";
                        else
                            childKey = newAlias + ".fk";
                        parentKey = currentAlias + ".id";

                        // Create the join operator
                        working = new Join(working, childOp, JoinType.INNER);
                        (working as Join).AddCondition(parentKey, Condition.NewCondition(ConditionType.EQ, new ColumnReference(childKey)));
                    }
                }
                else
                {
                    if (gqn.inQuery)
                        working = gqn.toOperator(c, working, projectList, ref currentAlias);
                }
            }

            return working;
        }

        private Operator createTableReference(Command c, ref string currentAlias)
        {
            // Create the initial reference
            currentAlias = c.nextTableAlias(this.g.GName);
            TableRef tr = new TableRef(this.g.GName, currentAlias);

            // Pull the column information and add to the table reference
            Scheme sch = AppData.sch[this.g.GName];
            foreach (Column col in sch.columns)
                tr.AddColumn(col.Name, currentAlias + "." + col.Name, col.dom);

            // Add references to added semantic columns, but only if they are relevant to the query (printed or filtered on)
            foreach (GQueryNode gqn in semanticAdditions)
                if (gqn.Print || gqn.gqCondition != null)
                    tr.AddColumn(gqn.g.GName, currentAlias + "." + gqn.g.GName, gqn.g.GDomain);

            return tr;
        }

        private GQueryNode pruneQueryMarks()
        {
            if (!(this.inQuery))
                return null;

            // This node should be the root if it is printed or has a condition associated with it
            if (this.Print || (this.gqCondition != null))
                return this;

            // This node should also be the root if it has more than one child node that is marked as relevant
            GQueryNode temp = null;
            foreach (GQueryNode gqn in this.Nodes)
            {
                // If this is the first that we've found, assign it
                if (gqn.inQuery && temp == null)
                    temp = gqn;

                // If this is not the first that we've found, we're the root
                else if (gqn.inQuery && temp != null)
                    return this;
            }

            // If we've gotten here and we have found a relevant child, process the child (and mark ourself irrelevant)
            if (temp != null)
            {
                this.inQuery = false;
                return temp.pruneQueryMarks();
            }

            // If we've gotten here at all, then there is are no relevant nodes and we can return null
            return null;
        }

        private void markQueryNodes()
        {
            bool foundChildRelevant = false;
            
            // Set the print flag, if necessary
            if (this.Checked)
            {
                this.Print = true;
            }
            else
                this.Print = false;

            // Recursive step - process children, and record if there are any relevant children
            foreach (GQueryNode temp in this.Nodes)
            {
                temp.markQueryNodes();
                if (temp.inQuery)
                    foundChildRelevant = true;
            }

            // Determine if this node should be considered for the query.  Consider a nodes as query-relevant if:
            // - It's already relevant (has a print or child condition
            // - It has a child that is relevant
            this.inQuery = foundChildRelevant || this.Print || (this.gqCondition != null);
        }





        public static Command toXmlQuery(XmlDocument queryTree)
        {
            Command command = new Command();
            List<Operator> operators = new List<Operator>();

            // Loop through each of the root nodes
            foreach (XmlElement temp in queryTree.FirstChild.ChildNodes)
            {
                // Recursively walk through the tree's nodes and process them
                markXmlNodes(queryTree, temp);

                // Prune the top of the tree so that there are no unnecessary joins at the top
                XmlElement queryRoot = pruneXmlQueryMarks(queryTree, temp);
                if (queryRoot == null) continue;

                // Make sure that the query root is a node of appropriate type
                while (queryRoot.Attributes["ControlType"].Value != "Entity" && queryRoot.Attributes["ControlType"].Value != "Extern")
                {
                    queryRoot = queryRoot.ParentNode as XmlElement;
                    if (queryRoot == null)
                        break;
                    queryRoot.Attributes["inQuery"].Value = "true";
                }
                if (queryRoot == null)
                    continue;

                // Create a new command to house the query
                List<string> projectList = new List<string>();
                string alias = null;
                Operator op = toXmlOperator(command, null, projectList, ref alias, queryRoot);
                operators.Add(new Project(op, projectList));
            }

            // If there are no operators in the list, return null
            if (operators.Count == 0)
                return null;

            // Otherwise, create the cross-product of the operators
            command.root = operators[0];
            for (int i = 1; i < operators.Count; i++)
                command.root = new CrossJoin(command.root, operators[i]);
            return command;
        }

        private static Operator toXmlOperator(Command c, Operator o, List<string> projectList, ref string currentAlias, XmlElement queryRoot)
        {
            // If the input parameter is null, then we're an entity-type node and we need to generate a table reference
            Operator working;
            if (o == null)
                working = createXmlTableReference(c, ref currentAlias, queryRoot);
            else
                working = o;

            // If we need to be printed, add the appropriate print flag
            if (queryRoot.Attributes["print"].Value == "true")
            {
                // If we have a print on a table node, add the id column
                if (queryRoot.Attributes["ControlType"].Value == "Entity" || queryRoot.Attributes["ControlType"].Value == "Extern")
                    projectList.Add("id");
                else
                    projectList.Add(queryRoot.Name);
            }

            // If we have a condition, add the condition as a filter node
            if (queryRoot.Attributes["condition"].Value != "")
            {
                working = new Filter(working);
                (working as Filter).AddCondition("id" , stringToCondition(queryRoot.Attributes["condition"].Value));
            }

            List<GQueryNode> toJoin = new List<GQueryNode>();
            foreach (XmlElement gqn in queryRoot.ChildNodes)
            {
                if (gqn.Attributes["ControlType"].Value == "Entity" || gqn.Attributes["ControlType"].Value == "Extern")
                {
                    if (gqn.Attributes["inQuery"].Value == "true")
                    {
                        // Process the child
                        string newAlias = null, childKey, parentKey;
                        Operator childOp = toXmlOperator(c, null, projectList, ref newAlias, gqn);

                        // Determine the join attributes
                        if (queryRoot.Attributes["launchType"].Value == "Single")
                            childKey = newAlias + ".id";
                        else
                            childKey = newAlias + ".fk";
                        parentKey = currentAlias + ".id";

                        // Create the join operator
                        working = new Join(working, childOp, JoinType.INNER);
                        (working as Join).AddCondition(parentKey, Condition.NewCondition(ConditionType.EQ, new ColumnReference(childKey)));
                    }
                }
                else
                {
                    if (gqn.Attributes["inQuery"].Value == "true")
                        working = toXmlOperator(c, working, projectList, ref currentAlias, gqn);
                }
            }

            return working;
        }

        private static Operator createXmlTableReference(Command c, ref string currentAlias, XmlElement node)
        {
            // Create the initial reference
            currentAlias = c.nextTableAlias(node.Name);
            TableRef tr = new TableRef(node.Name, currentAlias);
            tr.AddColumn("id", "id", GuavaData.Domain.Domain.FromXElement(XElement.Load(node.CreateNavigator().ReadSubtree())));
            /*/ Pull the column information and add to the table reference
            Scheme sch = AppData.sch[this.g.GName];
            foreach (Column col in sch.columns)
                tr.AddColumn(col.Name, currentAlias + "." + col.Name, col.dom);
            */
            foreach (XmlElement el in node.ChildNodes)
            {
                if (el.Attributes["ControlType"].Value == "Attribute" && el.Attributes["print"].Value == "true")
                    tr.AddColumn(el.Name, el.Name, GuavaData.Domain.Domain.FromXElement(XElement.Load(el.CreateNavigator().ReadSubtree())));
            }
            /*/ Add references to added semantic columns, but only if they are relevant to the query (printed or filtered on)
            foreach (GQueryNode gqn in semanticAdditions)
                if (gqn.Print || gqn.gqCondition != null)
                    tr.AddColumn(gqn.g.GName, currentAlias + "." + gqn.g.GName, gqn.g.GDomain);
            */
            return tr;
        }

        private static XmlElement pruneXmlQueryMarks(XmlDocument gtree, XmlElement node)
        {
            if (!(node.Attributes["inQuery"].Value == "true"))
                return null;

            // This node should be the root if it is printed or has a condition associated with it
            if (node.Attributes["print"].Value == "true" || (node.Attributes["condition"].Value != ""))
                return node;

            // This node should also be the root if it has more than one child node that is marked as relevant
            XmlElement temp = null;
            foreach (XmlElement gqn in node.ChildNodes)
            {
                // If this is the first that we've found, assign it
                if (gqn.Attributes["inQuery"].Value == "true" && temp == null)
                    temp = gqn;

                // If this is not the first that we've found, we're the root
                else if (gqn.Attributes["inQuery"].Value == "true" && temp != null)
                    return node;
            }

            // If we've gotten here and we have found a relevant child, process the child (and mark ourself irrelevant)
            if (temp != null)
            {
                node.Attributes["inQuery"].Value = "false";
                return pruneXmlQueryMarks(gtree, temp);
            }

            // If we've gotten here at all, then there is are no relevant nodes and we can return null
            return null;
        }

        private static void markXmlNodes(XmlDocument gtree, XmlElement node)
        {
            bool foundChildRelevant = false;
            
            /*/ Set the print flag, if necessary
            if (node.Attributes["print"].Value == "true")
            {
                this.Print = true;
            }
            else
                this.Print = false;
            */
            // Recursive step - process children, and record if there are any relevant children
            if (node.ChildNodes != null)
            {
                foreach (XmlNode temp in node.ChildNodes)
                {
                    if (temp is XmlElement && temp.Attributes != null)
                    {
                        markXmlNodes(gtree, temp as XmlElement);
                        if ((node.Name != "Domain" && node.Name != "Item") && temp.Attributes["inQuery"].Value == "true")
                            foundChildRelevant = true;
                    }
                }
            }
            // Determine if this node should be considered for the query.  Consider a nodes as query-relevant if:
            // - It's already relevant (has a print or child condition
            // - It has a child that is relevant
            

                XmlAttribute attr = gtree.CreateAttribute("inQuery");
                if ((node.Name != "Domain" && node.Name != "Item") &&(foundChildRelevant || node.Attributes["print"].Value == "true" || (node.Attributes["condition"].Value != "")))
                {
                    attr.Value = "true";
                }
                else attr.Value = "false";
                node.Attributes.Append(attr);
           
        }
        private static Condition stringToCondition(string cond)
        {
            return new NotNullCond(new List<object>());
        }
    }
}

